/*
===========================================================================
This source file is part of DyLab (Dynamic Laboratory)
For the latest info, see http://dylab.googlecode.com

Copyright (c) 2006-2008 Lukas Krejci
(krejci.lukas@volny.cz)

This file is part of DyLab.

    DyLab is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    DyLab is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with DyLab.  If not, see <http://www.gnu.org/licenses/>.
===========================================================================
*/

#ifndef __dylab_ComModel_h__
#define __dylab_ComModel_h__

//===========================================================================================
#include <dylab/simobject/ISimObjectComponent.h>
#include <dylab/utils/components/ComponentBase.h>
#include <dylab/utils/containers/HashObjContainer.h>
#include <dylab/utils/types/dynamicCast.h>

//===========================================================================================
namespace dylab {

    /** Model structure component type.
	@remarks
		This class defines an interface for the simulation object model structure component type.
		Model structure can be viewed from many perspectives,
		that are represented as interfaces, 
		which can be queried for support and retrieved.
		Components of this type are responsible for implementing the any number of model structure interfaces.
		However, technique and integrator components must be chosen accordingly,
		to use only the interfaces, that the current component implements.
    @warning 
		Never use pointers to the declared interfaces without 
		locking up the whole ComModelStructure::Instace object!
    */
    class DYLAB_EXPORT ComModelStructure  
		: public ISimObjectComponent
        , public ComponentBase<ComModelStructure>
    {
    public:
		/// Interface for all model structures
		class DYLAB_EXPORT IFeature
		{
		public:
			virtual ~IFeature()
					{ }
		};
		//-----------------------------------------------------------------------------

		/// Base class for all model structures
		template <class _FeatureClassT>
		class FeatureBase
			: virtual public IFeature
		{
		public:
			/// Shared scope lock with direct interface access.
			class ScopedShLock
				: public Instance::ScopedShLock_InternalObj<_FeatureClassT>
			{
			public:
				ScopedShLock(const Instance * _modelGuard)
					: Instance::ScopedShLock_InternalObj<_FeatureClassT>(_modelGuard)
						{ mInternalObjPtr = getImplFrom(mObjectPtr); }
			};
			//-----------------------------------------------------------------------------
		public:
			/// Exclussive scope lock with direct interface access.
			class ScopedExLock
				: public Instance::ScopedExLock_InternalObj<_FeatureClassT>
			{
			public:
				ScopedExLock(Instance * _modelGuard)
					: Instance::ScopedExLock_InternalObj<_FeatureClassT>(_modelGuard)
						{ mInternalObjPtr = getImplFrom(mObjectPtr); }
			};
			//-----------------------------------------------------------------------------
		public:
			///
			static String getName()
					{ return typeid(_FeatureClassT).raw_name(); }
			///
			static bool isImplementedIn(const ComModelStructure * _model)
					{ DYLAB_ASSERT(_model != NULL); 
						return _model->isFeatureImpl(getName()); }
			///
			static _FeatureClassT * getImplFrom(ComModelStructure * _model)
					{ DYLAB_ASSERT(_model != NULL);
						return dynamicCast<_FeatureClassT>(_model->getFeatureImpl(getName())); }
			///
			static const _FeatureClassT * getImplFrom(const ComModelStructure * _model)
					{ DYLAB_ASSERT(_model != NULL);
						return dynamicCast<const _FeatureClassT>(_model->getFeatureImpl(getName())); }
		public:
			FeatureBase(ComModelStructure * _parentModel)
					{ DYLAB_ASSERT(_parentModel != NULL); 
						_parentModel->setFeatureImpl(getName(), this); }
		};
		//-----------------------------------------------------------------------------

	private:
		// The container does not own the structure implementations.
		class DYLAB_EXPORT FeatureImplContainer
			: public HashObjContainer<String, IFeature>
		{
	    public:
			FeatureImplContainer()
				: HashObjContainer<String, IFeature>(false)
			        { }
		};
	    //-----------------------------------------------------------------------------

	private:
		FeatureImplContainer mFeatureImpls;

    public:
        ComModelStructure(const String & _componentName);

		/** Loads the model structure using the appropriate structure loader component.
		@remarks
			Loads the model structure with the data in the given property tree,
			which also contains the name of the model structure loader component.
		@param[in] _pt
			The property tree holding the data for this model structure.
		*/
		virtual void loadFromPropertyTree(const PropertyTree & _pt);

        /** The component should translate the whole model structure according to the given translate vector.
		@param[in] _trans
			The translate vector.
        */
        virtual void translate(const Vector3 & _trans) = 0;

        /** The component should rotate the whole model structure according to the given axis and angle.
		@param[in] _axis
			The rotation axis.
		@param[in] _radians
			The rotation angle in radians.
        */
        virtual void rotate(const Vector3 & _axis, real_t _radians) = 0;

        /** The component should scale the whole model structure according to the given scale vector.
		@param[in] _scale
			The scale vector.
        */
        virtual void scale(const Vector3 & _scale) = 0;

		/** */
		void setFeatureImpl(const String & _structureName, IFeature * _structure);

        /** Returns true, if it the interface with the given name is supported.
        */
        bool isFeatureImpl(const String & _structureName) const;

        /** Returns the pointer to an object implementing interface with the given name.
         */
        IFeature * getFeatureImpl(const String & _structureName);

        /** Returns the pointer to an object implementing interface with the given name.
         */
        const IFeature * getFeatureImpl(const String & _structureName) const;
    };
}
//===========================================================================================
#endif // __dylab_ComModel_h__
